﻿//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;

namespace Syrinx
{
	public class BaseDataScarfBizRules : BaseBizRules
	{
		static TimeSpan s_timeBetweenSaves = new TimeSpan(0, 0, 1, 0, 0);
		protected void writeTrace(IApplication app, IBizObj scarfInfo, string traceInfo, params object[] p)
		{
			string rt = string.Format(traceInfo, p);
			app.Trace.write(Swaf.Logging.TraceLevels.Normal, rt);
			lock (scarfInfo)
			{
				if (!(scarfInfo.get("runtime.traceLog") is StringBuilder))
					scarfInfo.put("runtime.traceLog", new StringBuilder());
				((StringBuilder)scarfInfo.get("runtime.traceLog")).AppendLine(rt);
				string saveRuntime = (string)scarfInfo.get("saveRuntimeLocation");
				if (!Swaf.Cmn.IsEmpty(saveRuntime) && DateTime.Now - (DateTime)scarfInfo.get("lastSaveTime") > s_timeBetweenSaves)
				{
					scarfInfo.put("lastSaveTime", DateTime.Now);
					System.IO.File.WriteAllText(app.resMgr.resolve(saveRuntime), ((IBizObj)scarfInfo.get("runtime")).Xml);
				}
			}
		}

		protected string loginUser(IBizObj scarfInfo)
		{

			HttpWebRequest req = (HttpWebRequest)WebRequest.Create(Application.currentApp.resMgr.resolve((string)scarfInfo.get("login.url"), new NameAddressSupportDictionaryWrapper(scarfInfo)));
			req.Method = "POST";
			req.ContentType = "application/x-www-form-urlencoded";
			req.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8 (.NET CLR 3.5.30729)";
			string extraPostContent = (string)scarfInfo.get("login.data");
			string postContent = string.Format("{4}{0}={1}&{2}={3}", scarfInfo.get("passwordFormField"), scarfInfo.get("sitePassword"), scarfInfo.get("userIdFormField"), scarfInfo.get("siteUserId"), extraPostContent);

			req.CookieContainer = new CookieContainer();
			CookieCollection cookies = scarfInfo.get("cookies") as CookieCollection;
			if (cookies != null)
				req.CookieContainer.Add(req.RequestUri, cookies);

			req.ContentLength = postContent.Length;
			Stream stmRequestStream = req.GetRequestStream();
			stmRequestStream.Write(Encoding.UTF8.GetBytes(postContent), 0, postContent.Length);
			stmRequestStream.Close();
			HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
			setupCookies(resp.Cookies, scarfInfo);

			Stream str = resp.GetResponseStream();
			string respOut = new StreamReader(str).ReadToEnd();
			str.Close();
			resp.Close();
			return respOut;
		}

		protected void setupCookies(CookieCollection curCookies, IBizObj scarfInfo)
		{
			CookieCollection cookies = scarfInfo.get("cookies") as CookieCollection;
			if (cookies == null)
			{
				cookies = curCookies;
				scarfInfo.put("cookies", cookies);
			}
			else
				cookies.Add(curCookies);

			//The following is for debug testing only so that we can review the cookies one by one.
			int numCookies = cookies.Count;
			for (int pos = 0; pos < numCookies; pos++)
			{
				Cookie c = cookies[pos];
				string name = c.Name;
				string v = c.Value;
			}
		}

		protected string fixupDateStr(string dateStr)
		{
			if (dateStr.Contains("Tod"))
				dateStr = dateStr.Replace("Today", DateTime.Now.ToShortDateString());
			else if (dateStr.Contains("Yest"))
				dateStr = dateStr.Replace("Yesterday", (DateTime.Now - new TimeSpan(1, 0, 0, 0, 0)).ToShortDateString());
			string[] parts = dateStr.Split(' ');
			if (parts.Length == 6 && parts[3] == "-")
				dateStr = string.Format("{0} {1} {2}, {3} {4}", parts[1], parts[0].Replace("th", "").Replace("st", "").Replace("nd", "").Replace("rd", ""), parts[2], parts[4], parts[5]);
			return dateStr;
		}

		protected string fixupUserName(string rawUserName)
		{
			string userName = rawUserName.Trim().Replace("_5F00_", "_");
			return userName;
		}

		protected string getPageText(string url, IBizObj scarfInfo)
		{
			return getPageText(url, scarfInfo, false, null);
		}

		protected string getPageText(IBizObj siteUrl, IBizObj scarfInfo, IDictionary ns, out string finalUrl, string parentUrl)
		{
			string url = (string)siteUrl.get("url");
			finalUrl = url = Application.currentApp.resMgr.resolve(url, ns);
			if (url.IndexOf(':') == -1 && !Cmn.IsEmpty(parentUrl))
			{
				int i = parentUrl.LastIndexOf('/');
				finalUrl = url = parentUrl.Substring(0, i + 1) + url;
			}
			string data = (string)siteUrl.get("data");
			data = Application.currentApp.resMgr.resolve(data, ns);
			return getPageText(url, scarfInfo, string.Compare("post", (string)siteUrl.get("type"), true) == 0, data);			
		}

		protected static Regex s_badFileChars = new Regex(@"[\?/\.&=]+");
		protected string getPageText(string url, IBizObj scarfInfo, bool usePost, string urlInfo)
		{
			string pageText = null;
			int attempts = 0;
			string cacheDir = (string)scarfInfo.get("pageCacheLocation");
			cacheDir = App.resMgr.resolve(cacheDir);
			string cacheFileName = null, fullFileName = null;
			bool usingCache = !Cmn.IsEmpty(cacheDir);

			string failureRs = (string)scarfInfo.get("pageLoadFailureRegex");
			Regex failureRegex = null;
			if (!Cmn.IsEmpty(failureRs))
				failureRegex = new Regex(failureRs, RegexOptions.Singleline);
	
			if (usingCache)
			{
				Uri loc = new Uri(url);
				cacheFileName = s_badFileChars.Replace(loc.PathAndQuery.Substring(1),"_") + ".htm";
				if(File.Exists(fullFileName = Cmn.pathCombine(cacheDir, cacheFileName)))
				{
					pageText = File.ReadAllText(fullFileName);
					if (failureRegex != null)
					{
						Match fm = failureRegex.Match(pageText);
						if (!fm.Success)
							pageText = null;
					}
					if(pageText != null)
					{
						lock (scarfInfo)
						{
							scarfInfo.put("runtime.totalPagesRetrievedFromCache", ((int)scarfInfo.get("runtime.totalPagesRetrievedFromCache")) + 1);
						}
						return pageText;
					}
				}
			}

			while (attempts++ < 4)
			{
				try
				{
					string actualUrl = url;
					if (!usePost && !Cmn.IsEmpty(urlInfo))
						if (!url.Contains("?"))
							actualUrl += "?" + urlInfo;
						else
							actualUrl += urlInfo;

					HttpWebRequest req = (HttpWebRequest)WebRequest.Create(actualUrl);
					if (usePost)
					{
						req.Method = "POST";
						req.ContentType = "application/x-www-form-urlencoded";
					}
                    string prefIP = (string)scarfInfo.get("preferredIpEndPoint");
                    if (!Cmn.IsEmpty(prefIP))
                    {
                        IPBinder binder = new IPBinder(prefIP);
                        req.ServicePoint.BindIPEndPointDelegate = new BindIPEndPoint(binder.BindIPEndPointCallback);
                    }

					req.CookieContainer = new CookieContainer(); 
					CookieCollection cookies = scarfInfo.get("cookies") as CookieCollection;
					if (cookies != null)
						req.CookieContainer.Add(req.RequestUri, cookies);

					req.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8 (.NET CLR 3.5.30729)";
					req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
					req.ServicePoint.ConnectionLimit = 16;

					if (usePost)
					{
						req.ContentLength = urlInfo.Length;
						Stream stmRequestStream = req.GetRequestStream();
						stmRequestStream.Write(Encoding.UTF8.GetBytes(urlInfo), 0, urlInfo.Length);
						stmRequestStream.Close();
					}

					HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
					Stream str = resp.GetResponseStream();
					pageText = new StreamReader(str).ReadToEnd();
					str.Close();
					resp.Close();
					lock (scarfInfo)
					{
						setupCookies(resp.Cookies, scarfInfo); 
						scarfInfo.put("runtime.totalPagesDownloaded", ((int)scarfInfo.get("runtime.totalPagesDownloaded")) + 1);
						scarfInfo.put("runtime.totalBytesDownloaded", ((long)scarfInfo.get("runtime.totalBytesDownloaded")) + pageText.Length);
					}
					break;
				}
				catch (Exception e)
				{
				}
			}

			if (failureRegex != null)
			{
				Match fm = failureRegex.Match(pageText);
				if (fm.Success)
					pageText = null;
			}
			if (usingCache && pageText != null)
			{
				Directory.CreateDirectory(cacheDir);
				File.WriteAllText(fullFileName, pageText);
			}

			return pageText;
		}

		protected bool downloadImageIntoCache(IApplication app, IBizObj scarfInfo, string origImg, string ourImg)
		{

			return downloadImageIntoCache(app, scarfInfo.get("cookies") as CookieCollection, scarfInfo.get("imageCache").ToString(),
				origImg, ourImg, (bool)scarfInfo.get("blindDownloadImages"));
		}
		protected bool downloadImageIntoCache(IApplication app, CookieCollection cookies, string imageCache, string origImg, string ourImg, bool blindDownload)
		{
            bool downloadImage = true;
            if (!blindDownload)
            {
                IResults rc = app.ruleMgr.execute("isImageInMediaCache", imageCache, ourImg);
                if (Cmn.IsTrue(rc.CallResults))
                    downloadImage = false;
            }

            if (downloadImage)
            {
                //Download image and add to desired media cache.
                IBizObj image = app.bizObjMgr.create("Image");
                image.put("file.name", ourImg);
                try
                {
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(origImg);
                    if (cookies != null)
                    {
                        req.CookieContainer = new CookieContainer();
                        req.CookieContainer.Add(req.RequestUri, cookies);
                    }
                    WebResponse resp = null;
                    Stream str = null;
                    try
                    {
                        resp = req.GetResponse();
                        str = resp.GetResponseStream();
                        Binary imgBin = new Binary(str, true);
                        image.put("file.data", imgBin);
                        IResults rc = app.ruleMgr.execute("addImage", imageCache, image);
                        return rc.ExceptionInfo == null;
                    }
                    finally
                    {
                        str.Close();
                        resp.Close();
                    }
                }
                catch (Exception)
                {
                }
            }
            return false;
		}
	}

    public class IPBinder
    {
        string m_prefIP = null;
        public IPBinder(string prefIP)
        {
            m_prefIP = prefIP;
        }
        public IPEndPoint BindIPEndPointCallback(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
        {
            if (m_prefIP != null && retryCount < 3)
                return new IPEndPoint(IPAddress.Parse(m_prefIP), 0);
            else
                return new IPEndPoint(IPAddress.Any, 0);
        }

    }
}
